package com.superjson.showpro.base;

import com.beantools.constants.SingleChar;
import com.beantools.util.StringUtil;
import org.hibernate.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created By zby on 12:25 2019/6/21
 * 基类数据访问层的实现类
 * 不过在新版本的<strong>hibernate</strong>中，需要加上注解{@link Transactional}
 *
 * @author baoya
 * @since 1.1.0
 */
@Transactional
@SuppressWarnings("all")
public class GenericDaoImpl<T> implements GenericDao<T> {
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 当前对象的类字面常量
     */
    private Class<T> persistentClass;

    /**
     * 通过依赖注入session工厂
     */
    @Resource
    private SessionFactory sessionFactory;


    /**
     * Created By zby on 12:28 2019/6/21
     * 通过构造器的方式获取具体数据访问层的实现类的类字面常量
     *
     * @param persistentClass 具体实现对象类字面常量
     */
    public GenericDaoImpl(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    @Autowired
    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * Created By zby on 12:29 2019/6/21
     * 获取非逻辑删除的对象
     *
     * @param objId 当前对象的id
     */
    @Override
    public T get(Long objId) {
        return (T) getSession().get(persistentClass, objId);
    }

    /**
     * Created By zby on 12:25 2019/6/21
     * 获取hibernate的session会话对象。其有两种获取方式
     * <pre>
     *     <ul>
     *         <ol>getCurrentSession 自动提交事务，并且关闭当前会话，释放资源</ol>
     *           <ol>openSession 需要手动关闭会话，释放资源</ol>
     *     </ul>
     * </pre>
     *
     * @return 返回session会话对象
     */
    public Session getSession() throws HibernateException {
        Session session = this.sessionFactory.getCurrentSession();
        if (session == null) {
            session = this.sessionFactory.openSession();
        }
        return session;
    }


    /**
     * Created By zby on 12:32 2019/6/21
     * 保存对象，其内部使用{@link Session#merge(Object)}方法，不用担心没有主键，或者
     * 主键没有创建的问题，并返回持久类的对象。
     *
     * @param object 当前对象
     */
    @Override
    public T save(T object) {
        Session session = getSession();
        return (T) session.merge(object);
    }

    /**
     * Created By zby on 12:34 2019/6/21
     * 原来类似{@link Session#merge(Object)}方法， 只不过，其不返回持久类对象
     *
     * @param object 当前对象
     */
    @Override
    public void update(T object) {
        Session session = getSession();
        session.saveOrUpdate(object);
    }

    /**
     * 将内存中对象清除，并从数据库获取最新的非逻辑删除对象，并放入缓存
     *
     * @param objId
     * @return
     */
    private T getLatestNLD(final Long objId) {
        T cacheObj;
        if ((cacheObj = this.get(objId)) != null) {
            this.getSession().evict(cacheObj);
            return this.get(objId);
        } else {
            return null;
        }
    }

    /**
     * 将内存中对象清除，并从数据库获取最新的对象并放入缓存
     *
     * @param objID
     * @return
     */
    public T getLatest(final Long objID) {
        return getLatestNLD(objID);
    }


    /**
     * Created By zby on 12:36 2019/6/21
     * 如果是逻辑删除对象，则更新is_deleted字段，如果是物理删除对象，直接删除该条记录
     */
    @Override
    public int delete(final Long id, final boolean isPhysicallyDelete) {
        if (!isPhysicallyDelete) {
            String hql = " update " + persistentClass.getSimpleName()
                    + " classObj set classObj.deleted=TRUE, classObj.modifyDatetime=:modifyDatetime, classObj.modifierType=:userType, classObj.modifierId=:userID"
                    + " where classObj.id =:objID and classObj.deleted = FALSE ";
            Query query = getSession().createQuery(hql);
            query.setLong("objID", id);
            query.setTimestamp("modifyDatetime", new Date());

            return query.executeUpdate();
        } else {
            Session session = getSession();
            Object obj = session.get(persistentClass, id);
            if (obj == null) {
                return 0;
            }
            session.delete(obj);

            return 1;
        }
    }

    /**
     * 查询ids对象
     *
     * @param ids
     * @return
     */
    @Override
    public List<T> get(final String ids) {
        if (StringUtil.isBlank(ids)) {
            return null;
        }
        Set set = new LinkedHashSet();
        if (ids.contains(SingleChar.SERIES_COMMA_OP)) {
            String[] vars = ids.split(SingleChar.SERIES_COMMA_OP);
            for (String var : vars) {
                if (StringUtil.isBlank(var)) {
                    continue;
                }
                Long id = Long.parseLong(var);
                set.add(get(id));
            }
        }
        return new ArrayList<>(set);
    }

    @Override
    public T get(Long objID, boolean isOperateNLDObj) {
        return null;
    }

    /**
     * Created By zby on 12:37 2019/6/21
     * 判断数据库是否存在
     */
    @SuppressWarnings("unchecked")
    @Override
    public boolean exists(final Long id) {
        Session session = getSession();
        IdentifierLoadAccess byId = session.byId(persistentClass);
        T entity = (T) byId.load(id);
//        数据库不存在，则返回false
        if (entity == null) {
            return false;
        }
        try {
            Method method = persistentClass.getMethod("isDeleted");
            method.setAccessible(true);
            return !(Boolean) method.invoke(entity);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException("反射调用" + persistentClass.getSimpleName() + "是否已逻辑删除方法失败！");
        }
    }
}
